﻿/********************************************************************
	created:	2011-8-16   15:30
	author:		Lifan Su
	
	purpose:	Implement of SHA-1 functions
*********************************************************************/

#include "sha1.h"
#include <cvt_int_mem.h>
#include "hex_to_char.h"

#include <assert.h>
#include <_stdint.h>
#include <string.h>


static const int blocksize = 0x40;
static const int blockmask = 0x3F;
static const int endmaxsize = 0x37;
static const uint8_t paddingch1 = 0x80;
static const uint8_t paddingcha = 0x00;

static uint32_t rotl(uint32_t val, uint32_t n);
static void block_calc(struct SHA1_c *val, const void *buf);


CRYPT_HASH_PREFIX void CRYPT_HASH_API sha1_d_read(struct SHA1_d *dst, const void *src)
{
    const uint8_t *_src = src;

    assert(dst && src && "Non-null pointer");

    dst->hash[0] = cvt_byte_to_u32_be(_src +  0);
    dst->hash[1] = cvt_byte_to_u32_be(_src +  4);
    dst->hash[2] = cvt_byte_to_u32_be(_src +  8);
    dst->hash[3] = cvt_byte_to_u32_be(_src + 12);
    dst->hash[4] = cvt_byte_to_u32_be(_src + 16);
}

CRYPT_HASH_PREFIX void CRYPT_HASH_API sha1_d_write(void *dst, const struct SHA1_d *src)
{
    uint8_t *_dst = dst;

    assert(dst && src && "Non-null pointer");

    cvt_u32_be_to_byte(_dst +  0, src->hash[0]);
    cvt_u32_be_to_byte(_dst +  4, src->hash[1]);
    cvt_u32_be_to_byte(_dst +  8, src->hash[2]);
    cvt_u32_be_to_byte(_dst + 12, src->hash[3]);
    cvt_u32_be_to_byte(_dst + 16, src->hash[4]);
}

CRYPT_HASH_PREFIX void CRYPT_HASH_API sha1_d_print(char *dst, const struct SHA1_d *src)
{
    int i, j;

    assert(dst && src && "Non-null pointer");

    for (i = 0; i != 5; ++i) {
        for (j = 8; j != 0; --j) {
            *(dst++) = hex_to_char(((src->hash[i]) >> (4 * j - 4)) & 0xF);
        }
    }

    *dst = '\0';
}

CRYPT_HASH_PREFIX void CRYPT_HASH_API sha1_d_printw(wchar_t *dst, const struct SHA1_d *src)
{
    int i, j;

    assert(dst && src && "Non-null pointer");

    for (i = 0; i != 5; ++i) {
        for (j = 8; j != 0; --j) {
            *(dst++) = hex_to_wchar(((src->hash[i]) >> (4 * j - 4)) & 0xF);
        }
    }

    *dst = L'\0';
}

CRYPT_HASH_PREFIX uint8_t CRYPT_HASH_API sha1_d_less(const struct SHA1_d *lhs, const struct SHA1_d *rhs)
{
    assert(lhs && rhs && "Non-null pointer");

    return (lhs->hash[0] < rhs->hash[0]) || ((lhs->hash[0] == rhs->hash[0]) 
        && ((lhs->hash[1] < lhs->hash[1]) || ((lhs->hash[1] == rhs->hash[1])
        && ((lhs->hash[2] < rhs->hash[2]) || ((lhs->hash[2] == rhs->hash[2])
        && ((lhs->hash[3] < rhs->hash[3]) || ((lhs->hash[3] == rhs->hash[3])
        && (lhs->hash[4] < rhs->hash[4]))))))));
}

CRYPT_HASH_PREFIX uint8_t CRYPT_HASH_API sha1_d_eq(const struct SHA1_d *lhs, const struct SHA1_d *rhs)
{
    assert(lhs && rhs && "Non-null pointer");

    return (lhs->hash[0] == rhs->hash[0]) && (lhs->hash[1] == rhs->hash[1])
        && (lhs->hash[2] == rhs->hash[2]) && (lhs->hash[3] == rhs->hash[3])
        && (lhs->hash[4] == rhs->hash[4]);
}

CRYPT_HASH_PREFIX void CRYPT_HASH_API sha1_cvt(struct SHA1_d *dst, const struct SHA1_c *src)
{
    int i;

    assert(dst && src && "Non-null pointer");

    for (i = 0; i != 5; ++i) {
        dst->hash[i] = src->hash[i];
    }
}

CRYPT_HASH_PREFIX void CRYPT_HASH_API sha1_c_init(struct SHA1_c *val)
{
    assert(val && "Non-null pointer");

    val->hash[0] = UINT32_C(0x67452301);
    val->hash[1] = UINT32_C(0xefcdab89);
    val->hash[2] = UINT32_C(0x98badcfe);
    val->hash[3] = UINT32_C(0x10325476);
    val->hash[4] = UINT32_C(0xC3D2E1F0);

    val->length = 0;
    val->append = 1;
}

CRYPT_HASH_PREFIX void CRYPT_HASH_API sha1_c_append(struct SHA1_c *val, const void *src, uint64_t len)
{
    int buffer_used;
    const uint8_t *_src = src;

    assert(val && src && val->append && "Non-null pointer and append able");

    buffer_used = val->length & blockmask;
    val->length += len;
    while (len >= blocksize - buffer_used) {
        if (buffer_used) {
            memcpy(val->buffer + buffer_used, _src, blocksize - buffer_used);
            block_calc(val, val->buffer);
            _src += (blocksize - buffer_used);
            len -= (blocksize - buffer_used);
            buffer_used = 0;
        } else {
            block_calc(val, _src);
            _src += blocksize;
            len -= blocksize;
        }
    }

    memcpy(val->buffer + buffer_used, _src, (size_t)len);
}

CRYPT_HASH_PREFIX void CRYPT_HASH_API sha1_c_close(struct SHA1_c *val)
{

    assert(val && val->append && "Non-null pointer and append able");

    if ((val->length & blockmask) <= endmaxsize) {
        val->buffer[val->length & blockmask] = paddingch1;
        memset(val->buffer + (val->length & blockmask) + 1, paddingcha,
            endmaxsize - (val->length & blockmask));
        cvt_u64_be_to_byte(val->buffer + endmaxsize + 1, val->length << 3);
        block_calc(val, val->buffer);
    } else {
        val->buffer[val->length & blockmask] = paddingch1;
        memset(val->buffer + (val->length & blockmask) + 1, paddingcha,
            blocksize - 1 - (val->length & blockmask));
        block_calc(val, val->buffer);
        memset(val->buffer,  paddingcha, blocksize - 8);
        cvt_u64_be_to_byte(val->buffer + endmaxsize + 1, val->length << 3);
        block_calc(val, val->buffer);
    }

    val->append = 0;
}


static uint32_t rotl(uint32_t val, uint32_t n)
{
    return (val << n) | (val >> (32-n));
}

static void block_calc(struct SHA1_c *val, const void *buf)
{
    int i;
    uint32_t f, temp, k;
    const uint8_t *_buf;

    uint32_t words_arr[80];
    uint32_t buf_arr[5];

    _buf = buf;

    for (i = 0; i != 16; ++i) {
        words_arr[i] = cvt_byte_to_u32_be(_buf + i * 4);
    }
    for (i = 16; i != 32; ++i) {
        words_arr[i] = rotl(words_arr[i - 3] ^ words_arr[i - 8]
        ^ words_arr[i - 14] ^ words_arr[i - 16], 1);
    }
    for (i = 32; i != 80; ++i) {
        words_arr[i] = rotl(words_arr[i - 6] ^ words_arr[i - 16]
        ^ words_arr[i - 28] ^ words_arr[i - 32], 2);
    }
    for (i = 0; i != 5; ++i) {
        buf_arr[i] = val->hash[i];
    }
    

    k = UINT32_C(0x5A827999);
    for (i = 0; i != 20; ++i) {
        f = (buf_arr[1] & buf_arr[2]) | ((~buf_arr[1]) & buf_arr[3]);
        temp = rotl(buf_arr[0], 5) + f + buf_arr[4] + k + words_arr[i];
        buf_arr[4] = buf_arr[3];
        buf_arr[3] = buf_arr[2];
        buf_arr[2] = rotl(buf_arr[1], 30);
        buf_arr[1] = buf_arr[0];
        buf_arr[0] = temp;
    }

    k = UINT32_C(0x6ED9EBA1);
    for (i = 20; i != 40; ++i) {
        f = buf_arr[1] ^ buf_arr[2] ^ buf_arr[3];
        temp = rotl(buf_arr[0], 5) + f + buf_arr[4] + k + words_arr[i];
        buf_arr[4] = buf_arr[3];
        buf_arr[3] = buf_arr[2];
        buf_arr[2] = rotl(buf_arr[1], 30);
        buf_arr[1] = buf_arr[0];
        buf_arr[0] = temp;
    }

    k = UINT32_C(0x8F1BBCDC);
    for (i = 40; i != 60; ++i) {
        f = (buf_arr[1] & buf_arr[2]) | (buf_arr[2] & buf_arr[3])
            | (buf_arr[3] & buf_arr[1]);
        temp = rotl(buf_arr[0], 5) + f + buf_arr[4] + k + words_arr[i];
        buf_arr[4] = buf_arr[3];
        buf_arr[3] = buf_arr[2];
        buf_arr[2] = rotl(buf_arr[1], 30);
        buf_arr[1] = buf_arr[0];
        buf_arr[0] = temp;
    }

    k = UINT32_C(0xCA62C1D6);
    for (i = 60; i != 80; ++i) {
        f = buf_arr[1] ^ buf_arr[2] ^ buf_arr[3];
        temp = rotl(buf_arr[0], 5) + f + buf_arr[4] + k + words_arr[i];
        buf_arr[4] = buf_arr[3];
        buf_arr[3] = buf_arr[2];
        buf_arr[2] = rotl(buf_arr[1], 30);
        buf_arr[1] = buf_arr[0];
        buf_arr[0] = temp;
    }
    
    for (i = 0; i != 5; ++i) {
        val->hash[i] += buf_arr[i];
    }
}